package com.bxmdm.ragdemo.service;

import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingClient;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.vectorstore.SearchRequest;
// 注释掉 Ollama 相关导入，改用 DeepSeek
// import org.springframework.ai.ollama.OllamaChatClient;
// import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.bxmdm.ragdemo.dto.QAPair;
import com.bxmdm.ragdemo.dto.RagSearchResult;
import com.bxmdm.ragdemo.reader.EnglishQAReader;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class EnglishQAService {

    private final VectorStore vectorStore;
    private final EmbeddingClient embeddingClient;
    private final ResourceLoader resourceLoader;
    // 注释掉 OllamaChatClient，改用 DeepseekService
    // private final OllamaChatClient ollamaChatClient;
    private final DeepseekService deepseekService;

    public EnglishQAService(VectorStore vectorStore, EmbeddingClient embeddingClient,
            ResourceLoader resourceLoader, DeepseekService deepseekService) {
        this.vectorStore = vectorStore;
        this.embeddingClient = embeddingClient;
        this.resourceLoader = resourceLoader;
        // 注释掉 OllamaChatClient，改用 DeepseekService
        // this.ollamaChatClient = ollamaChatClient;
        this.deepseekService = deepseekService;
    }

    /**
     * 从文件导入问答数据到向量数据库
     * 
     * @param file JSON格式的问答数据文件
     * @return 导入的记录数量
     */
    public Map<String, Integer> importFromFile(MultipartFile file) {
        Path tempFile = null;
        try {
            tempFile = Files.createTempFile("qa-import-", ".json");
            try (InputStream in = file.getInputStream()) {
                Files.copy(in, tempFile, StandardCopyOption.REPLACE_EXISTING);
            }
            Map<String, Integer> stats = importFromLocalFile(tempFile.toAbsolutePath().toString());
            return stats;
        } catch (IOException e) {
            throw new RuntimeException("导入问答数据失败: " + e.getMessage(), e);
        } finally {
            if (tempFile != null) {
                try {
                    Files.deleteIfExists(tempFile);
                } catch (IOException ignore) {
                }
            }
        }
    }

    /**
     * 从本地路径导入问答数据
     * 
     * @param filePath 本地文件路径
     * @return 导入的记录数量
     */
    public Map<String, Integer> importFromLocalFile(String filePath) {
        Path tempFile = null;
        try {
            // 复制到临时文件，避免文件占用冲突
            tempFile = Files.createTempFile("qa-import-", ".json");
            Files.copy(new File(filePath).toPath(), tempFile, StandardCopyOption.REPLACE_EXISTING);

            Resource resource = resourceLoader.getResource("file:" + tempFile.toAbsolutePath());

            // 使用EnglishQAReader读取并处理数据
            EnglishQAReader qaReader = new EnglishQAReader(resource, embeddingClient);

            // 直接获取Document列表并写入向量数据库
            List<Document> documents = qaReader.get();
            vectorStore.add(documents);

            Map<String, Integer> stats = new HashMap<>();
            stats.put("total", documents.size());
            stats.put("questions", documents.size());

            return stats;
        } catch (IOException e) {
            throw new RuntimeException("从本地文件导入问答数据失败: " + e.getMessage(), e);
        } finally {
            if (tempFile != null) {
                try {
                    Files.deleteIfExists(tempFile);
                } catch (IOException ignore) {
                }
            }
        }
    }

    /**
     * 通用RAG搜索方法：统一处理语义搜索和降级搜索逻辑，消除重复代码
     * 
     * @param question                       用户问题
     * @param topK                           返回结果数量
     * @param calculateSimilarityForFallback 是否为降级搜索结果计算相似度
     * @return 包含相似度信息的问答对列表
     */
    private List<QAPair> performRAGSearch(String question, int topK, boolean calculateSimilarityForFallback) {
        System.out.println("  执行通用RAG搜索...");

        // 使用优化后的搜索获取相关问答对
        List<QAPair> qaPairs = semanticSearch(question, topK);
        System.out.println("  语义搜索结果数量: " + qaPairs.size());

        // 如果使用semanticSearch方法获取的结果不足，尝试使用更宽松的搜索条件
        if (qaPairs.isEmpty() || qaPairs.size() < topK / 2) {
            System.out.println("  语义搜索结果不足，启用降级搜索...");
            List<Document> fallbackDocs = fallbackKeywordSearch(question, topK);
            System.out.println("  降级搜索结果数量: " + fallbackDocs.size());

            if (!fallbackDocs.isEmpty()) {
                List<Double> queryEmbedding = calculateSimilarityForFallback ? embeddingClient.embed(question) : null;

                List<QAPair> fallbackPairs = fallbackDocs.stream()
                        .map(doc -> convertDocumentToQAPair(doc, queryEmbedding))
                        .collect(Collectors.toList());

                // 如果原来结果为空，直接使用降级结果
                if (qaPairs.isEmpty()) {
                    qaPairs = fallbackPairs;
                    System.out.println("  使用降级搜索结果");
                } else {
                    // 否则合并两个结果列表，确保没有重复
                    Set<String> existingQuestions = qaPairs.stream()
                            .map(QAPair::getQuestion)
                            .collect(Collectors.toSet());

                    int originalSize = qaPairs.size();
                    for (QAPair pair : fallbackPairs) {
                        if (!existingQuestions.contains(pair.getQuestion())) {
                            qaPairs.add(pair);
                            if (qaPairs.size() >= topK)
                                break;
                        }
                    }
                    System.out.println("  合并结果，从 " + originalSize + " 增加到 " + qaPairs.size());
                }
            } else {
                System.out.println("  降级搜索也未找到结果");
            }
        }

        return qaPairs;
    }

    /**
     * RAG检索问答：基于语义相似度检索相关问答并按分数排序返回
     * 使用与ragSearch相同的核心逻辑，但返回RagSearchResult格式
     *
     * @param question 用户问题
     * @param topK     返回的最大结果数量
     * @return 格式化的RAG检索结果对象，包含数量和结果列表
     */
    public RagSearchResult ragAnswer(String question, int topK) {
        System.out.println("=== RAG检索开始 ===");
        System.out.println("原始问题: " + question);
        System.out.println("请求数量: " + topK);

        // 使用与ragSearch相同的搜索逻辑，但不计算降级搜索的相似度（保持ragSearch行为）
        List<QAPair> qaPairs = performRAGSearch(question, topK, false);

        // 如果没有结果，返回空的RagSearchResult
        if (qaPairs.isEmpty()) {
            System.out.println("=== RAG检索完成（无结果）===");
            return new RagSearchResult(0, new ArrayList<>());
        }

        // 转换为RagSearchResult格式
        List<RagSearchResult.RagSearchItem> resultItems = new ArrayList<>();

        System.out.println("最终返回结果数量: " + qaPairs.size());
        System.out.println("最终结果预览:");

        for (int i = 0; i < qaPairs.size(); i++) {
            QAPair pair = qaPairs.get(i);
            // 对于语义搜索的结果，使用已有的相似度；对于降级搜索的结果，相似度可能为0
            double similarity = pair.getSimilarity();

            System.out.println("  结果" + (i + 1) + ": [相似度: " + String.format("%.3f", similarity) + "] "
                    + pair.getQuestion().substring(0, Math.min(80, pair.getQuestion().length())) + "...");

            // 处理其他答案
            List<String> otherAnswers = new ArrayList<>();
            if (pair.getAllAnswers() != null && pair.getAllAnswers().size() > 1) {
                otherAnswers = pair.getAllAnswers().stream()
                        .filter(answer -> !answer.equals(pair.getAnswer()))
                        .limit(3) // 最多显示3个其他答案
                        .map(answer -> answer.length() > 200 ? answer.substring(0, 200) + "..." : answer)
                        .collect(Collectors.toList());
            }

            RagSearchResult.RagSearchItem item = new RagSearchResult.RagSearchItem(
                    similarity,
                    pair.getQuestion(),
                    pair.getAnswer(),
                    otherAnswers);
            resultItems.add(item);
        }

        System.out.println("=== RAG检索完成 ===\n");
        return new RagSearchResult(resultItems.size(), resultItems);
    }

    /**
     * 语义检索相关问答（RAG增强版）
     * 
     * @param question 用户问题
     * @param topK     返回结果数量
     * @return 相关问答对
     */
    public List<QAPair> semanticSearch(String question, int topK) {
        logDebugInfo("执行语义搜索...");
        logDebugInfo("原始查询: %s", question);

        // 获取查询向量
        List<Double> queryEmbedding = embeddingClient.embed(question);
        logDebugInfo("查询向量维度: %d", queryEmbedding.size());

        // 配置搜索请求，使用相似度搜索
        SearchRequest searchRequest = SearchRequest.query(question)
                .withTopK(topK * 2) // 先检索更多结果
                .withSimilarityThreshold(0.1); // 设置相似度阈值

        // 获取搜索结果
        List<Document> docs = vectorStore.similaritySearch(searchRequest);
        logDebugInfo("向量搜索返回文档数: %d", docs.size());

        // 计算真实的embedding相似度并排序
        List<QAPair> result = docs.stream()
                .map(doc -> convertDocumentToQAPair(doc, queryEmbedding))
                .sorted((a, b) -> Double.compare(b.getSimilarity(), a.getSimilarity())) // 按相似度降序排序
                .limit(topK)
                .collect(Collectors.toList());

        logSearchResults("语义搜索", result);
        return result;
    }

    /**
     * 计算embedding向量相似度
     */
    private double calculateEmbeddingSimilarity(List<Double> queryEmbedding, String targetText) {
        try {
            // 获取目标文本的embedding向量
            List<Double> targetEmbedding = embeddingClient.embed(targetText);

            // 计算余弦相似度
            return calculateCosineDistance(queryEmbedding, targetEmbedding);
        } catch (Exception e) {
            System.err.println("计算embedding相似度失败: " + e.getMessage());
            return 0.0;
        }
    }

    /**
     * 计算两个向量的余弦相似度
     */
    private double calculateCosineDistance(List<Double> vector1, List<Double> vector2) {
        if (vector1.size() != vector2.size()) {
            throw new IllegalArgumentException("向量维度不匹配");
        }

        double dotProduct = 0.0;
        double norm1 = 0.0;
        double norm2 = 0.0;

        for (int i = 0; i < vector1.size(); i++) {
            dotProduct += vector1.get(i) * vector2.get(i);
            norm1 += vector1.get(i) * vector1.get(i);
            norm2 += vector2.get(i) * vector2.get(i);
        }

        if (norm1 == 0.0 || norm2 == 0.0) {
            return 0.0;
        }

        return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
    }

    /**
     * 降级搜索方法，当向量搜索效果不佳时使用
     */
    private List<Document> fallbackKeywordSearch(String query, int topK) {
        System.out.println("  降级搜索详情:");
        // 提取查询中的关键词
        String[] keywords = query.toLowerCase()
                .replaceAll("[^a-zA-Z0-9\\s]", " ")
                .split("\\s+");

        System.out.println("  提取的关键词: " + String.join(", ", keywords));

        // 使用简单的关键词匹配，不依赖向量相似度
        List<Document> allDocs = vectorStore.similaritySearch(SearchRequest.query("question").withTopK(100));
        System.out.println("  从向量库获取的候选文档数: " + allDocs.size());

        List<Document> filtered = allDocs.stream()
                .filter(doc -> {
                    // 检查文档是否包含查询中的关键词
                    String content = doc.getContent().toLowerCase();
                    for (String keyword : keywords) {
                        if (keyword.length() > 2 && content.contains(keyword)) {
                            return true;
                        }
                    }
                    return false;
                })
                .limit(topK)
                .collect(Collectors.toList());
        System.out.println("  关键词匹配后的文档数: " + filtered.size());
        return filtered;
    }

    /**
     * 将文档转换为QAPair对象的通用方法
     * 
     * @param doc            文档对象
     * @param queryEmbedding 查询向量（可选，用于计算相似度）
     * @return QAPair对象
     */
    private QAPair convertDocumentToQAPair(Document doc, List<Double> queryEmbedding) {
        Map<String, Object> meta = doc.getMetadata();
        String question = (String) meta.getOrDefault("question", doc.getContent());
        String answer = (String) meta.get("answer");
        @SuppressWarnings("unchecked")
        List<String> allAnswers = (List<String>) meta.getOrDefault("all_answers", new ArrayList<>());

        QAPair pair = new QAPair(question, answer, allAnswers);

        // 如果提供了查询向量，计算相似度
        if (queryEmbedding != null) {
            double similarity = calculateEmbeddingSimilarity(queryEmbedding, question);
            pair.setSimilarity(similarity);
        }

        return pair;
    }

    /**
     * 统一的调试输出方法
     */
    private void logDebugInfo(String message, Object... params) {
        if (params.length > 0) {
            System.out.printf("  " + message + "%n", params);
        } else {
            System.out.println("  " + message);
        }
    }

    /**
     * 输出搜索结果预览
     */
    private void logSearchResults(String title, List<QAPair> results) {
        logDebugInfo("%s结果数量: %d", title, results.size());
        logDebugInfo("%s结果预览:", title);
        for (int i = 0; i < Math.min(3, results.size()); i++) {
            QAPair pair = results.get(i);
            String preview = pair.getQuestion().substring(0, Math.min(60, pair.getQuestion().length())) + "...";
            if (pair.getSimilarity() > 0) {
                logDebugInfo("  %d. [相似度: %.3f] %s", i + 1, pair.getSimilarity(), preview);
            } else {
                logDebugInfo("  %d. %s", i + 1, preview);
            }
        }
    }
}